import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Scanner;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.AllocateAddressResult;
import com.amazonaws.services.ec2.model.AssociateAddressRequest;
import com.amazonaws.services.ec2.model.AttachVolumeRequest;
import com.amazonaws.services.ec2.model.AuthorizeSecurityGroupIngressRequest;
import com.amazonaws.services.ec2.model.CreateKeyPairRequest;
import com.amazonaws.services.ec2.model.CreateKeyPairResult;
import com.amazonaws.services.ec2.model.CreateSecurityGroupRequest;
import com.amazonaws.services.ec2.model.CreateTagsRequest;
import com.amazonaws.services.ec2.model.CreateVolumeRequest;
import com.amazonaws.services.ec2.model.CreateVolumeResult;
import com.amazonaws.services.ec2.model.DescribeAvailabilityZonesResult;
import com.amazonaws.services.ec2.model.DescribeImagesResult;
import com.amazonaws.services.ec2.model.DescribeInstancesRequest;
import com.amazonaws.services.ec2.model.DescribeInstancesResult;
import com.amazonaws.services.ec2.model.DescribeKeyPairsRequest;
import com.amazonaws.services.ec2.model.DescribeKeyPairsResult;
import com.amazonaws.services.ec2.model.DescribeSecurityGroupsResult;
import com.amazonaws.services.ec2.model.Image;
import com.amazonaws.services.ec2.model.Instance;
import com.amazonaws.services.ec2.model.InstanceState;
import com.amazonaws.services.ec2.model.IpPermission;
import com.amazonaws.services.ec2.model.KeyPair;
import com.amazonaws.services.ec2.model.KeyPairInfo;
import com.amazonaws.services.ec2.model.Placement;
import com.amazonaws.services.ec2.model.Reservation;
import com.amazonaws.services.ec2.model.RunInstancesRequest;
import com.amazonaws.services.ec2.model.RunInstancesResult;
import com.amazonaws.services.ec2.model.SecurityGroup;
import com.amazonaws.services.ec2.model.StartInstancesRequest;
import com.amazonaws.services.ec2.model.StopInstancesRequest;
import com.amazonaws.services.ec2.model.Tag;
import com.amazonaws.services.ec2.model.TerminateInstancesRequest;
import com.amazonaws.services.s3.model.Bucket;
import com.amazonaws.services.s3.model.S3Object;
import java.awt.Label;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;


public class Hub {

    /*
     * Important: Be sure to fill in your AWS access credentials in the
     *            AwsCredentials.properties file before you try to run this
     *            sample.
     * http://aws.amazon.com/security-credentials
     */

    public AmazonEC2 ec2;
    public AWSCredentials credentials;
    public void initializing(String kid,String seck) throws IOException {

    
    	
    	

    	 	 credentials = new BasicAWSCredentials(kid,seck);

    	 	/*********************************************
    	 	* 
    	 	*  #1 Create Amazon Client object
    	 	*  
    	 	*********************************************/
    	 	System.out.println("#1 Creating Amazon Client object for user: "+credentials.getAWSAccessKeyId());
    	 	ec2 = new AmazonEC2Client(credentials);

    	 	
    	 	//Storage newStorage = new Storage(credentials);
    	 
    	 	//initDB newDB = new initDB(credentials);
    	 	
       
    	 try {
        	final String groupName = "groupname";
        	final String keyName = "keypair";
        	/*********************************************
        	 * 
             *  #2 Describe Availability Zones.
             *  
             *********************************************/
        	System.out.println("#2 Describe Availability Zones for user: "+credentials.getAWSAccessKeyId());
            DescribeAvailabilityZonesResult availabilityZonesResult = ec2.describeAvailabilityZones();
            System.out.println("You have access to " + availabilityZonesResult.getAvailabilityZones().size() + " Availability Zones.");

            /*********************************************
             * 
             *  #3 Describe Available Images
             *  
             *********************************************/
            System.out.println("#3 Describe Available Images for user: "+credentials.getAWSAccessKeyId());
            DescribeImagesResult dir = ec2.describeImages();
            List<Image> images = dir.getImages();
            System.out.println("You have " + images.size() + " Amazon images");
            
            
            /*********************************************
             *                 
             *  #4 Describe Key Pair
             *                 
             *********************************************/
            System.out.println("#4 Describe Key Pair for user: "+credentials.getAWSAccessKeyId());
         
            DescribeKeyPairsResult dkr = ec2.describeKeyPairs();
            //Print Key Pairs
            ListIterator<KeyPairInfo> keyPairs = dkr.getKeyPairs().listIterator();
            boolean existKeyPair = false;
            while(keyPairs.hasNext()){
            	KeyPairInfo keyPair = keyPairs.next();
            	if(keyName.equals(keyPair.getKeyName())){
            		System.out.println("You have Key Pair name is " + keyPair.getKeyName());
            		existKeyPair = true;
            	}
            }
 
            if(existKeyPair==false){
            	CreateKeyPairRequest ckpr = new CreateKeyPairRequest();
            	ckpr.setKeyName(keyName);
            	CreateKeyPairResult ckpres = ec2.createKeyPair(ckpr);
            	KeyPair kp = ckpres.getKeyPair();
            	
            	System.out.println("You have a new Key Pair: " + kp.getKeyName());
            	System.out.println("The Key Pair finger print is: " + kp.getKeyFingerprint());
            	System.out.println("The Key Pair marterial is: " + kp.getKeyMaterial());
            }
            
            System.out.println(dkr.toString());
            
            
            /*********************************************
             *                 
             *  #5 Create Security Groups
             *                 
             *********************************************/
            System.out.println("#5 Create Sercurity Group for user: "+credentials.getAWSAccessKeyId());
            
            boolean hasSecGrp = false;
	        DescribeSecurityGroupsResult descSecGrp = ec2.describeSecurityGroups();
	        ListIterator<SecurityGroup> secGrpList = descSecGrp.getSecurityGroups().listIterator();
	        String secGrpId  = null;
	        while (secGrpList.hasNext())
	        {
	        	SecurityGroup secGrp = secGrpList.next();
	        	if (secGrp.getGroupName().equals(groupName))
	        	{
	        		hasSecGrp = true;
	        		secGrpId=secGrp.getGroupId();
	        	}
	        }
	        
	        if (hasSecGrp)
	        	System.out.println("Security Group: " + groupName + " already exists. Security Group is created.");
	        else{
            	CreateSecurityGroupRequest csgr = new CreateSecurityGroupRequest();
            	csgr.setGroupName(groupName);
            	csgr.setDescription("Cloud Computing Assignment 2");
            	secGrpId = ec2.createSecurityGroup(csgr).getGroupId();
            	System.out.println("You have create a security group named: " + groupName);
            }
            
            System.out.println("Setting protocols for HTTP, SSH and HTTPS for user: "+credentials.getAWSAccessKeyId());
            
            AuthorizeSecurityGroupIngressRequest asgir = new AuthorizeSecurityGroupIngressRequest();
            asgir.setGroupId(secGrpId);
            
           ArrayList<IpPermission> ipPerms = new ArrayList<IpPermission>();
            ArrayList<String> ipRanges = new ArrayList<String>();
            
            ipRanges.add("0.0.0.0/0");
            
            //set ssh  protocol
            IpPermission sshPerm = new IpPermission();
            sshPerm.setIpProtocol("tcp");
            sshPerm.setFromPort(22);
            sshPerm.setToPort(22);
            sshPerm.setIpRanges(ipRanges);
            
            //set http protocol
            IpPermission httpPerm = new IpPermission();
            httpPerm.setIpProtocol("tcp");
            httpPerm.setFromPort(80);
            httpPerm.setToPort(80);
            httpPerm.setIpRanges(ipRanges);
            
            //set https protocol
            IpPermission httpsPerm = new IpPermission();
            httpsPerm.setIpProtocol("tcp");
            httpsPerm.setFromPort(443);
            httpsPerm.setToPort(443);
            httpsPerm.setIpRanges(ipRanges);
            
            ipPerms.add(sshPerm);
            ipPerms.add(httpPerm);
            ipPerms.add(httpsPerm);
            
            asgir.setIpPermissions(ipPerms);   
          

            ArrayList<String> secGroup = new ArrayList<String>();
            secGroup.add(secGrpId);                              
           
            
            /*********************************************
             * 
             *  #6 Describe Current Instances
             *  
             *********************************************/
            System.out.println("#6 Describe Current Instances for user: "+credentials.getAWSAccessKeyId());
            DescribeInstancesResult describeInstancesRequest = ec2.describeInstances();
            List<Reservation> reservations = describeInstancesRequest.getReservations();
            Set<Instance> instances = new HashSet<Instance>();
            // add all instances to a Set.
            for (Reservation reservation : reservations) {
            	instances.addAll(reservation.getInstances());
            }
            
            System.out.println("You have " + instances.size() + " Amazon EC2 instance(s).");
            
            for (Instance ins : instances){
            	
            	// instance id
            	String instanceId = ins.getInstanceId();
            	
            	// instance state
            	InstanceState is = ins.getState();
            	System.out.println(instanceId+" "+is.getName());
            	
            	
            }

             String insId=null;
             DataBase db=new DataBase(credentials);
     		if(!db.Exist(DataBase.table))
     			db.createTable(DataBase.table);
     		System.out.println(db.Exist(DataBase.table));
             User u=User.getUser(credentials);

             if(u==null){
            	
            	 /*********************************************
                 * 
                 *  #7 Create an Instance
                 *  
                 *********************************************/
            	 
            	MyInstance newInstance = new MyInstance(ec2, credentials, keyName ,secGroup);
                newInstance.createInstance();
                insId = newInstance.getInstanceId();
                System.out.println("Create INSTANCE" + insId+" for user: "+credentials.getAWSAccessKeyId());
                
                /*********************************************
                 * 
                 *  #7 Create and attach an EBS
                 *  
                 *********************************************/
                //Create EBS
                
                
                Placement placement;
                placement = newInstance.runningInstance.getPlacement();
                String zone = placement.getAvailabilityZone();
                CreateVolumeRequest cvr = new CreateVolumeRequest();
    	        cvr.setAvailabilityZone(zone);
    	        cvr.setSize(1); //size = 1 gigabytes
            	CreateVolumeResult volumeResult = ec2.createVolume(cvr);
            	String createdVolumeId = volumeResult.getVolume().getVolumeId();
            	
            	
            	
            	//Attach EBS to instance
            	AttachVolumeRequest avr = new AttachVolumeRequest();
            	avr.setVolumeId(createdVolumeId);
            	avr.setInstanceId(newInstance.getInstanceId());
            	avr.setDevice("/dev/sdf");
            	ec2.attachVolume(avr);
                
            	
            	 /*********************************************
                 * 
                 *  #7 Allocate and associate IP Addr
                 *  
                 *********************************************/
            	//allocate
            	AllocateAddressResult elasticResult = ec2.allocateAddress();
            	String elasticIp = elasticResult.getPublicIp();
            	
            	System.out.println("The new elastic IP is: " + elasticIp);
            	
            	//associate
            	AssociateAddressRequest aar = new AssociateAddressRequest();
            	aar.setInstanceId(newInstance.getInstanceId());
            	aar.setPublicIp(elasticIp);
            	ec2.associateAddress(aar);
            	
            	 /*********************************************
                 * 
                 *  #7 Create Storage S3
                 *  
                 *********************************************/
                //Create Storage S3
            	Storage stor = new Storage(credentials);
           
            	//Create Bucket for S3
            	if(stor.getBucket(credentials.getAWSAccessKeyId())==null){
            		stor.createBucket(credentials.getAWSAccessKeyId().toLowerCase());
            		stor.writeBucket(credentials.getAWSAccessKeyId().toLowerCase());
            	}
            	
            	
            	u=new User(credentials, elasticIp, createdVolumeId);
            	
            }
            
          else{
            	 String oldIp;
            	 String oldAWSAccessKeyId;
            	 String oldAMIid;
            	 String oldVolumeId;
            	 
            	 credentials = u.getCredentials();
            	 oldIp = u.getIp();
            	 oldAWSAccessKeyId = u.getAWSAccessKeyId();
            	 oldAMIid = u.getAMIid();
            	 oldVolumeId = u.getVolumnId();

            	 
            	
            	 
            	
            	 //restoring instance from AMI
            	String imageId = oldAMIid;
         		int minInstanceCount = 1;
         		int maxInstanceCount = 1;
         		
         		
 //        		AttachVolumeRequest avr = new AttachVolumeRequest();
//             	avr.setVolumeId(oldVolumeId);
//             	avr.setInstanceId(instanceId);
//             	avr.setDevice("/dev/sdf");
 //            	ec2.attachVolume(avr);
         		
         		
         		RunInstancesRequest rir = new RunInstancesRequest(imageId, minInstanceCount, maxInstanceCount);
                rir.setKeyName(keyName);
                rir.setSecurityGroupIds(secGroup);
         		rir.setInstanceType("m1.small");
         		rir.setPlacement(new Placement("us-east-1a"));
         		
         		
         		
         		RunInstancesResult result = ec2.runInstances(rir);

         		
         		
         		List<Instance> resultInstance = result.getReservation().getInstances();
         		
         		String instanceId = null;
                 for (Instance ins : resultInstance){
                 	instanceId = ins.getInstanceId();
                	
                 	System.out.println("New instance has been created: "+ins.getInstanceId()+" for user: "+credentials.getAWSAccessKeyId());
                 }
                 
                 String state = "";
                 DescribeInstancesRequest direq = new DescribeInstancesRequest();
                 List<String> resous = new LinkedList<String>();
                 resous.add(instanceId);
                 direq.setInstanceIds(resous);
                 Instance in = null;
                 Thread.sleep(10000);
                 while(state.equals("running")==false){
                 	DescribeInstancesResult dires = ec2.describeInstances(direq);
                 	List<Reservation> resers = dires.getReservations();
                 	Reservation reser = (Reservation) resers.get(0);
                 	in = reser.getInstances().get(0);
                 	InstanceState ins = in.getState();
                 	state = ins.getName();
                 	System.out.println("The instance is: " + state);
                 	Thread.sleep(10000);
                 }
                 System.out.println("The instance is: " + state);
                 System.out.println(in.toString());
                 
                AttachVolumeRequest avr = new AttachVolumeRequest();
              	avr.setVolumeId(u.getVolumnId());
              	avr.setInstanceId(in.getInstanceId());
              	avr.setDevice("/dev/sdf");
              	ec2.attachVolume(avr);
              	insId = in.getInstanceId();
                 
                 
                 List<String> resources = new LinkedList<String>();
                 List<Tag> tags = new LinkedList<Tag>();
                 Tag nameTag = new Tag("Name", "CloudComputing");
                 
                 resources.add(instanceId);
                 tags.add(nameTag);
                 
                 CreateTagsRequest ctr = new CreateTagsRequest(resources, tags);
                 ec2.createTags(ctr);
            	 
                 //restore EBS
             	//Attach EBS to instance
             	
             	
             	//associate the old IPs 
            	AssociateAddressRequest aar = new AssociateAddressRequest();
            	aar.setInstanceId(instanceId);
            	aar.setPublicIp(oldIp);
            	ec2.associateAddress(aar);
            	 
            	//restore S3
            	Storage newStorage = new Storage(credentials);
            	Bucket oldBucket = newStorage.getBucket(credentials.getAWSAccessKeyId().toLowerCase());
                newStorage.outputBucket(oldBucket);
             }       
            
            
            
            
            
           
        	
        	 /*********************************************
             * 
             *  #7 Monitor User
             *  
             *********************************************/
            //monitor User
             ExecutorService exec=Executors.newCachedThreadPool();
		     exec.execute(new Monitor(insId, this, u));
		     exec.shutdown();
        /*	
        	boolean r = newCloudWatch.returnstop();
        	while(r){
        		 r = newCloudWatch.returnstop();
        	}*/
            
        	 /*********************************************
             * 
             *  #7 Create AMI
             *  
             *********************************************/
                        
            
        } catch (AmazonServiceException ase) {
                System.out.println("Caught Exception: " + ase.getMessage());
                System.out.println("Reponse Status Code: " + ase.getStatusCode());
                System.out.println("Error Code: " + ase.getErrorCode());
                System.out.println("Request ID: " + ase.getRequestId());
        } catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}

        
    }
    
    public static void main(String... args){
    	Hub a=new Hub();
    	Hub b=new Hub();
    	try {
			a.initializing("XXX", "X");
			b.initializing("YYY", "Y");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}    	
    }
    
    class keyListener extends JFrame implements Runnable,KeyListener {

		/**
		 * 
		 */
		private static final long serialVersionUID = 1L;

		@Override
		public void run() {
            System.out.println("waiting back for user: "+Hub.this.credentials.getAWSAccessKeyId());
            this.setSize(300,50);
            java.awt.Dimension winSize = Toolkit.getDefaultToolkit().getScreenSize();
            this.setLocation((winSize.width-this.getWidth())/2,(winSize.height-this.getHeight())/2); 
            this.add(new Label("Press any key when user "+Hub.this.credentials.getAWSAccessKeyId()+" back"));
            this.addKeyListener(this);           
		}
		
        @Override
        public void keyPressed(KeyEvent e) 
        {

        	   try {
               	System.out.println("user "+Hub.this.credentials.getAWSAccessKeyId()+" return");
               	this.removeKeyListener(this);
               	this.dispose();
   				Hub.this.initializing(Hub.this.credentials.getAWSAccessKeyId(),Hub.this.credentials.getAWSSecretKey());
   			} catch (IOException err) {

   				err.printStackTrace();
   			}

        }

        @Override
        public void keyReleased(KeyEvent e) {
	    // TODO Auto-generated method stub
	    }

        @Override
        public void keyTyped(KeyEvent e) {
	    // TODO Auto-generated method stub	
        }	
    }

}